// Copyright 2016 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_CORE_DECODER_BUFFER_H_
#define DRACO_CORE_DECODER_BUFFER_H_

#include <stdint.h>
#include <cstring>
#include <memory>

#include "draco/draco_features.h"

#include "draco/core/macros.h"

namespace draco {

// Class is a wrapper around input data used by MeshDecoder. It provides a
// basic interface for decoding either typed or variable-bit sized data.
class DecoderBuffer {
  public:
    DecoderBuffer();
    DecoderBuffer(const DecoderBuffer &buf) = default;

    DecoderBuffer &operator=(const DecoderBuffer &buf) = default;

    // Sets the buffer's internal data. Note that no copy of the input data is
    // made so the data owner needs to keep the data valid and unchanged for
    // runtime of the decoder.
    void Init(const char *data, size_t data_size);

    // Sets the buffer's internal data. |version| is the Draco bitstream version.
    void Init(const char *data, size_t data_size, uint16_t version);

    // Starts decoding a bit sequence.
    // decode_size must be true if the size of the encoded bit data was included,
    // during encoding. The size is then returned to out_size.
    // Returns false on error.
    bool StartBitDecoding(bool decode_size, uint64_t *out_size);

    // Ends the decoding of the bit sequence and return to the default
    // byte-aligned decoding.
    void EndBitDecoding();

    // Decodes up to 32 bits into out_val. Can be called only in between
    // StartBitDecoding and EndBitDecoding. Otherwise returns false.
    bool DecodeLeastSignificantBits32(int nbits, uint32_t *out_value) {
        if (!bit_decoder_active())
            return false;
        bit_decoder_.GetBits(nbits, out_value);
        return true;
    }

    // Decodes an arbitrary data type.
    // Can be used only when we are not decoding a bit-sequence.
    // Returns false on error.
    template <typename T>
    bool Decode(T *out_val) {
        if (!Peek(out_val))
            return false;
        pos_ += sizeof(T);
        return true;
    }

    bool Decode(void *out_data, size_t size_to_decode) {
        if (data_size_ < static_cast<int64_t>(pos_ + size_to_decode))
            return false;  // Buffer overflow.
        memcpy(out_data, (data_ + pos_), size_to_decode);
        pos_ += size_to_decode;
        return true;
    }

    // Decodes an arbitrary data, but does not advance the reading position.
    template <typename T>
    bool Peek(T *out_val) {
        const size_t size_to_decode = sizeof(T);
        if (data_size_ < static_cast<int64_t>(pos_ + size_to_decode))
            return false;  // Buffer overflow.
        memcpy(out_val, (data_ + pos_), size_to_decode);
        return true;
    }

    bool Peek(void *out_data, size_t size_to_peek) {
        if (data_size_ < static_cast<int64_t>(pos_ + size_to_peek))
            return false;  // Buffer overflow.
        memcpy(out_data, (data_ + pos_), size_to_peek);
        return true;
    }

    // Discards #bytes from the input buffer.
    void Advance(int64_t bytes) {
        pos_ += bytes;
    }

    // Moves the parsing position to a specific offset from the beginning of the
    // input data.
    void StartDecodingFrom(int64_t offset) {
        pos_ = offset;
    }

    void set_bitstream_version(uint16_t version) {
        bitstream_version_ = version;
    }

    // Returns the data array at the current decoder position.
    const char *data_head() const {
        return data_ + pos_;
    }
    int64_t remaining_size() const {
        return data_size_ - pos_;
    }
    int64_t decoded_size() const {
        return pos_;
    }
    bool bit_decoder_active() const {
        return bit_mode_;
    }

    // Returns the bitstream associated with the data. Returns 0 if unknown.
    uint16_t bitstream_version() const {
        return bitstream_version_;
    }

  private:
    // Internal helper class to decode bits from a bit buffer.
    class BitDecoder {
      public:
        BitDecoder();
        ~BitDecoder();

        // Sets the bit buffer to |b|. |s| is the size of |b| in bytes.
        inline void reset(const void *b, size_t s) {
            bit_offset_ = 0;
            bit_buffer_ = static_cast<const uint8_t *>(b);
            bit_buffer_end_ = bit_buffer_ + s;
        }

        // Returns number of bits decoded so far.
        inline uint64_t BitsDecoded() const {
            return static_cast<uint64_t>(bit_offset_);
        }

        // Return number of bits available for decoding
        inline uint64_t AvailBits() const {
            return ((bit_buffer_end_ - bit_buffer_) * 8) - bit_offset_;
        }

        inline uint32_t EnsureBits(int k) {
            DRACO_DCHECK_LE(k, 24);
            DRACO_DCHECK_LE(static_cast<uint64_t>(k), AvailBits());

            uint32_t buf = 0;
            for (int i = 0; i < k; ++i) {
                buf |= PeekBit(i) << i;
            }
            return buf;  // Okay to return extra bits
        }

        inline void ConsumeBits(int k) {
            bit_offset_ += k;
        }

        // Returns |nbits| bits in |x|.
        inline bool GetBits(int32_t nbits, uint32_t *x) {
            DRACO_DCHECK_GE(nbits, 0);
            DRACO_DCHECK_LE(nbits, 32);
            uint32_t value = 0;
            for (int32_t bit = 0; bit < nbits; ++bit)
                value |= GetBit() << bit;
            *x = value;
            return true;
        }

      private:
        // TODO(fgalligan): Add support for error reporting on range check.
        // Returns one bit from the bit buffer.
        inline int GetBit() {
            const size_t off = bit_offset_;
            const size_t byte_offset = off >> 3;
            const int bit_shift = static_cast<int>(off & 0x7);
            if (bit_buffer_ + byte_offset < bit_buffer_end_) {
                const int bit = (bit_buffer_[byte_offset] >> bit_shift) & 1;
                bit_offset_ = off + 1;
                return bit;
            }
            return 0;
        }

        inline int PeekBit(int offset) {
            const size_t off = bit_offset_ + offset;
            const size_t byte_offset = off >> 3;
            const int bit_shift = static_cast<int>(off & 0x7);
            if (bit_buffer_ + byte_offset < bit_buffer_end_) {
                const int bit = (bit_buffer_[byte_offset] >> bit_shift) & 1;
                return bit;
            }
            return 0;
        }

        const uint8_t *bit_buffer_;
        const uint8_t *bit_buffer_end_;
        size_t bit_offset_;
    };
    friend class BufferBitCodingTest;

    const char *data_;
    int64_t data_size_;

    // Current parsing position of the decoder.
    int64_t pos_;
    BitDecoder bit_decoder_;
    bool bit_mode_;
    uint16_t bitstream_version_;
};

}  // namespace draco

#endif  // DRACO_CORE_DECODER_BUFFER_H_
